package cn.net.cfss.fgbp.gateway.filter;

import cn.net.cfss.fgbp.base.common.GlobalConstants;
import cn.net.cfss.fgbp.base.constant.ErrCodeEnum;
import cn.net.cfss.fgbp.base.except.CodeException;
import cn.net.cfss.fgbp.base.util.IpUtils;
import cn.net.cfss.fgbp.base.vo.BaseRespVo;
import cn.net.cfss.fgbp.base.vo.ReturnVo;
import cn.net.cfss.fgbp.base.vo.UserVo;
import cn.net.cfss.fgbp.gateway.common.Constants;
import cn.net.cfss.fgbp.gateway.config.ConfigProperty;
import cn.net.cfss.fgbp.gateway.service.VerifyService;
import cn.net.cfss.fgbp.gateway.util.ReturnUtils;
import cn.net.cfss.fgbp.gateway.vo.GateWayApiDto;
import cn.net.cfss.fgbp.log.common.ELogLevelEnum;
import cn.net.cfss.fgbp.log.statistics.IStatsdLogService;
import cn.net.cfss.fgbp.log.statistics.LogDtoFactory;
import cn.net.cfss.fgbp.log.util.LogStandardUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.URLEncoder;
import java.util.concurrent.locks.ReentrantLock;

@Component
@Slf4j
public class AuthFilter implements GlobalFilter, Ordered {

    @Autowired
    private IStatsdLogService iStatsdLogService;
    private ReentrantLock lock=new ReentrantLock();
    @Override
    public int getOrder() {
        // 指定此过滤器位于NettyWriteResponseFilter之后
        // 即待处理完响应体后接着处理响应头
        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
    }


    @Autowired
    VerifyService verifyService;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        verifyService.createApiDto(exchange.getRequest());
        try {
            //跳过不需要验证的路径
            Mono<Void> result = verifyService.skipAuth( chain, exchange);
            if (result != null) {
                return result.then(Mono.fromRunnable(() -> {
                    log(exchange,ELogLevelEnum.INFO,null);
                }));
            }

            //从请求头中取出token
            String token = exchange.getRequest().getHeaders().getFirst(GlobalConstants.HEADER_AUTHORIZATION);
            //未携带token或token在黑名单内
            if (token == null || token.isEmpty()) {
                throw new CodeException(ErrCodeEnum.E401);
            }
            //用户认证
            //UserVo userVo = verifyService.verify(token);


            //验证通过，更新redis
            UserVo userVo = verifyService.updateTokenTime(token);

            //从请求头中取出门户第二token，如果该token不为空，那么刷新该token的过期时间
            String secondToken = exchange.getRequest().getHeaders().getFirst(GlobalConstants.HEADER_SECOND_TOKEN);
            if(!StringUtils.isEmpty(secondToken)){
                verifyService.updateSecondTokenTime(secondToken);
            }

            //用户鉴权
            // verifyService.auth(userVo, url);
            ServerHttpRequest.Builder builder = exchange.getRequest().mutate();

            setHead(builder, GlobalConstants.HEADER_LOGIN_NAME, userVo.getLoginName());
            setHead(builder, GlobalConstants.HEADER_USER_MOBILE, userVo.getCellPhone());
            setHead(builder, GlobalConstants.HEADER_USER_EMAIL, userVo.getEmail());
            setHead(builder, GlobalConstants.HEADER_USER_SPID, userVo.getSpId() + "");
            setHead(builder, GlobalConstants.HEADER_USER_NAME, URLEncoder.encode(userVo.getUserName(), "UTF-8"));
            setHead(builder, GlobalConstants.HEADER_USERID, userVo.getUserId() + "");
            setHead(builder, GlobalConstants.HEADER_ACCOUNT_TYPE, userVo.getAccountType() + "");
            setHead(builder, GlobalConstants.HEADER_USER_CUSTID, userVo.getCustId() + "");
            setHead(builder, GlobalConstants.HEADER_USER_MERCHANTID, userVo.getMerchantId() + "");
            setHead(builder, GlobalConstants.HEADER_USER_ENTID, userVo.getEntId() + "");
            setHead(builder, GlobalConstants.HEADER_USER_COMPANYID, userVo.getCompanyId() + "");
            setHead(builder, GlobalConstants.HEADER_USER_COMPANY_NAME, URLEncoder.encode(userVo.getCompanyName() + "", "UTF-8"));
            setHead(builder, GlobalConstants.HEADER_CHANNEL_CODE, userVo.getChannelCode() + "");
            setHead(builder, GlobalConstants.HEADER_AUTHORIZATION, userVo.getNewToken());
            setHead(builder, GlobalConstants.HEADER_USER_BUSINESSID, String.valueOf(userVo.getBusinessId()));
           // setHead(builder, GlobalConstants.HEADER_GLOBAL_REQID, verifyService.getApiDto(exchange.getRequest()).getTrace());
           // setHead(builder, GlobalConstants.HEADER_GLOBAL_SPANID, verifyService.getApiDto(exchange.getRequest()).getSpan());
            setHead(builder, GlobalConstants.HEADER_PROJECT_IDS, userVo.getProjectIds());
            setHead(builder, GlobalConstants.HEADER_FEIGN_CLIENT, ConfigProperty.getIns().appName);
            setHead(builder, GlobalConstants.HEADER_OPS_ACCOUNT_ID, userVo.getOpsAccountId() + "");
            setHead(builder, GlobalConstants.HEADER_USER_TENANT_ID, userVo.getTenantId() + "");
            ServerWebExchange mutableExchange = exchange.mutate().request(builder.build()).build();
            ServerHttpResponse response = mutableExchange.getResponse();
            HttpHeaders httpHeaders = response.getHeaders();
            httpHeaders.add(GlobalConstants.HEADER_AUTHORIZATION, userVo.getNewToken());
            httpHeaders.add("Access-Control-Expose-Headers", GlobalConstants.HEADER_AUTHORIZATION.concat(",")
                    .concat(GlobalConstants.HEADER_CONTENT_DISPOSITION).concat(",")
                    .concat(GlobalConstants.HEADER_ROUTE_VERSION).concat(",")
                    .concat(GlobalConstants.HEADER_N_D_VERSION).concat(",")
                    .concat(GlobalConstants.HEADER_MOBILE));
            Mono<Void> ret = chain.filter(mutableExchange);

            return ret.then(Mono.fromRunnable(() -> {
                log(exchange,ELogLevelEnum.INFO,null);

            }));
        } catch (CodeException codeException) {
           log(exchange,ELogLevelEnum.ERROR,ExceptionUtils.getFullStackTrace(codeException));
            return ReturnUtils.returnErr(exchange.getResponse(), new ReturnVo(new BaseRespVo(codeException.getErrCodeEnum(), codeException.getDetail())));
        } catch (Exception exception) {
            log(exchange,ELogLevelEnum.ERROR,ExceptionUtils.getFullStackTrace(exception));
            return ReturnUtils.returnErr(exchange.getResponse(), new ReturnVo(new BaseRespVo(ErrCodeEnum.ERR, exception.getMessage())));
        }

    }

    private void setHead(ServerHttpRequest.Builder builder, String key, String val) {
        if (!StringUtils.isEmpty(val)) {
            builder.header(key, val);
        }
    }


    private  void log(ServerWebExchange exchange,ELogLevelEnum level,String errMsg)
    {
            lock.lock();
            try {
                long begin = 0;
                if (exchange.getRequest().mutate().build().getHeaders().getFirst(Constants.H_BEGIN_TIMESTAMP) != null) {
                    begin = Long.valueOf(exchange.getRequest().mutate().build().getHeaders().getFirst(Constants.H_BEGIN_TIMESTAMP));
                }
                long cost = System.currentTimeMillis() - begin;
                LogStandardUtil.extendLogV3(exchange.getRequest().mutate().build().getHeaders().getFirst(GlobalConstants.HEADER_GLOBAL_REQID),
                        exchange.getRequest().mutate().build().getHeaders().getFirst(GlobalConstants.HEADER_GLOBAL_SPANID),
                        "AuthFilter", "filter", "", IpUtils.getIpAddr(exchange.getRequest()), "",
                        String.valueOf(cost)
                        , ConfigProperty.getIns().appVersion);
                if (level == ELogLevelEnum.INFO) {
                    log.info("url[{}],trace=[{}],return ", exchange.getRequest().getURI().getPath(), exchange.getRequest().mutate().build().getHeaders().getFirst(GlobalConstants.HEADER_GLOBAL_REQID));
                } else {
                    log.error("网关错误:url={},错误信息={}", exchange.getRequest().getURI().getPath(), errMsg);
                }
                GateWayApiDto apiDto = LogDtoFactory.getNewApiDto(exchange.getRequest().mutate().build().getHeaders().getFirst(GlobalConstants.HEADER_GLOBAL_REQID)
                        , exchange.getRequest().mutate().build().getHeaders().getFirst(Constants.H_SERVER_NAME)
                        , ConfigProperty.getIns().appName, IpUtils.getIpAddr(exchange.getRequest()), exchange.getRequest().mutate().build().getHeaders().getFirst(Constants.H_METHOD_NAME), ConfigProperty.getIns().appVersion, null, level,
                        exchange.getRequest().mutate().build().getHeaders().getFirst(GlobalConstants.HEADER_GLOBAL_SPANID), GateWayApiDto.class);
                apiDto.setCost(cost);
                iStatsdLogService.saveLog(apiDto);
            }
            catch (Exception e) {
                log.error("err="+e.getMessage());
            }
            finally {
                lock.unlock();
        }
    }
}